home *** CD-ROM | disk | FTP | other *** search
- ;=========================================================================
- ; XSCALE1.ASM by John A. Slagel, jas37876@uxa.cso.uiuc.edu
- ; This is some code to do bitmap scaling in VGA Mode X. It can scale a
- ; bitmap of any size down to 2 pixels wide, or up to thousands of pixels
- ; wide. It performs complete clipping, with only a small constant amount
- ; of time to clip, no matter how huge the image is. It draws column by
- ; column to reduce the number of plane switches, which are slow. The inner
- ; column loop has been optimized for no memory accesses, except to read or
- ; write a pixel. This uses MASM 5.1 features, and can be compiled in any
- ; memory model by changing the .MODEL line, but make sure that you always
- ; pass a far pointer to the bitmap data, regardless of memory model.
- ; C-callable as:
- ; void XSCALE1( int X, int Y, int DW, int DY,
- ; int SW, int SH, void far * Bitmap );
- ; X,Y are the upper left-hand coordinates of where to draw the bitmap.
- ; DW,DH are the width and height of the SCALEed bitmap
- ; SW,SH are the width and height of the source bitmap.
- ; Bitmap is a pointer to the bitmap bits.
- ;
- ;==========================================================================
- .MODEL LARGE, C
- .386
-
- .DATA
- SC_INDEX1 EQU 03C4h ; Port number of VGA Sequencer Reg
- SC_INDEX2 EQU 03C5h ; Port number of VGA Seqeuncer Data
- MAP_MASK EQU 2 ; Map Mask register index into Sequencer
-
- ClipLt DW 20 ; Left clipping boundry
- ClipRt DW 300 ; Right clipping boundry
- ClipTp DW 20 ; Top clipping boundry
- ClipBt DW 190 ; Bottom clipping boundry
- ModeXseg DW 0A000h ; Current VGA segment
-
- .CODE
-
- XSCALE1 PROC FAR USES DS DI SI, DestX:WORD, DestY:WORD, \
- DestWidth:WORD, DestHeight:WORD, \
- SourceWidth:WORD, SourceHeight:WORD, \
- Bitmap:FAR PTR
- LOCAL DecisionX:WORD, DecisionY:WORD, ClippedWidth:WORD, ClippedHeight:WORD
-
- cmp DestWidth, 2 ; If destination width is less than 2
- jl Done ; then don't draw it.
-
- cmp DestHeight, 2 ; If destination height is less than 2
- jl Done ; then don't draw it.
-
- mov ax, DestY ; If it is completely below the
- cmp ax, ClipBt ; lower clip bondry,
- jg Done ; then don't draw it.
-
- add ax, DestHeight ; If it is above clip boundries
- dec ax ; then don't draw it.
- cmp ax, ClipTp
- jl Done
-
- mov ax, DestX ; If it is to the right of the
- mov cx, ClipRt ; right clip boundry
- cmp ax, ClipRt ; then don't draw it.
- jg Done
-
- add ax, DestWidth ; If it is completely to the left
- dec ax ; of the left clip boundry,
- cmp ax, ClipLt ; then don't draw it.
- jl Done
-
- les si, Bitmap ; Make DS:SI point to bitmap data
-
- mov ax, DestWidth ; ClippedWidth is initially set to
- mov ClippedWidth, ax ; the requested dest width.
-
- shl ax,1 ; Initialize the X decision var
- neg ax ; to be -2*DestWidth
- mov DecisionX, ax ;
-
- mov ax, DestHeight ; ClippedHeight is initially set to
- mov ClippedHeight, ax ; the requested dest size.
-
- shl ax,1 ; Initialize the Y decision var
- neg ax ; to be -2*DestHeight
- mov DecisionY, ax ;
-
- movsx eax, ClipTp ; If Y is below the top
- mov edx, eax ; clipping boundry, then we don't
- sub dx, DestY ; need to clip the top, so we can
- js NoTopClip ; jump over the clipping stuff.
-
- mov DestY, ax ; This block performs clipping on the
- sub ClippedHeight, dx ; top of the bitmap. I have heavily
- movsx ecx, SourceHeight ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestHeight ; this: The DecisionY var is updated
- idiv ebx ; to start at the right clipped row.
- movsx edx, SourceWidth ; Y is moved to the top clip
- imul edx, eax ; boundry. ClippedHeight is lowered since
- add si, dx ; we won't be drawing all the requested
- imul eax, ebx ; rows. SI is changed to point over
- sub ecx, eax ; the bitmap data that is clipped off.
- sub ecx, ebx ;
- shl ecx, 1 ;
- mov DecisionY, cx ; <end of top clipping block >
-
- NoTopClip:
- mov ax, DestY ; If the bitmap doesn't extend over the
- add ax, ClippedHeight ; bottom clipping boundry, then we
- dec ax ; don't need to clip the bottom, so we
- cmp ax, ClipBt ; can jump over the bottom clip code.
- jle NoBottomClip ;
-
- mov ax, ClipBt ; Clip off the bottom by reducing the
- sub ax, DestY ; ClippedHeight so that the bitmap won't
- inc ax ; extend over the lower clipping
- mov ClippedHeight, ax ; boundry.
-
- NoBottomClip:
- movsx eax, ClipLt ; If X is to the left of the
- mov edx, eax ; top clipping boundry, then we don't
- sub dx, DestX ; need to clip the left, so we can
- js NoLeftClip ; jump over the clipping stuff.
-
- mov DestX, ax ; This block performs clipping on the
- sub ClippedWidth, dx ; left of the bitmap. I have heavily
- movsx ecx, SourceWidth ; optimized this block to use only 4
- imul ecx, edx ; 32-bit registers, so I'm not even
- mov eax, ecx ; gonna try to explain what it's doing.
- mov edx, 0 ; But I can tell you what results from
- movsx ebx, DestWidth ; this: The DecisionX var is updated
- idiv ebx ; to start at the right clipped column.
- add si, ax ; X is moved to the left clip
- imul eax, ebx ; boundry. ClippedWidth is reduced since
- sub ecx, eax ; we won't be drawing all the requested
- sub ecx, ebx ; cols. SI is changed to point over
- shl ecx, 1 ; the bitmap data that is clipped off.
- mov DecisionX, cx ; <end of left clipping block >
-
- NoLeftClip:
- mov ax, DestX ; If the bitmap doesn't extend over the
- add ax, ClippedWidth ; right clipping boundry, then we
- dec ax ; don't need to clip the right, so we
- cmp ax, ClipRt ; can jump over the right clip code.
- jle NoClipRight ;
-
- mov ax, ClipRt ; Clip off the right by reducing the
- sub ax, DestX ; ClippedWidth so that the bitmap won't
- inc ax ; extend over the right clipping
- mov ClippedWidth, ax ; boundry.
-
- ;Calculate starting video address
- NoClipRight:
- mov ax, ModeXseg ; We are going to set DS:DI to point
- mov ds, ax ; to the place to start drawing in
- mov di, DestY ; VGA memory. This code sets DS to the
- imul di, 80 ; VGA segment, which is usually at
- mov ax, DestX ; segment 0A000. The offset DI is
- mov cx, ax ; calculated by:
- shr ax, 2 ; DI = Y*80+X/2
- add di, ax ; DS:DI is ready!
-
- mov dx, SC_INDEX1 ; Point the VGA Sequencer to the Map
- mov al, MAP_MASK ; Mask register, so that we only need
- out dx, al ; to send out 1 byte per column.
-
- inc dx ; Move to the Sequencer's Data register.
- and cx, 3 ; Calculate the starting plane. This is
- mov al, 11h ; just:
- shl al, cl ; Plane = (11h << (X AND 3))
- out dx, al ; Select the first plane.
-
- ALIGN 4 ; Since this point gets jumped to a lot,
- ; make sure that it is DWORD aligned.
- RowLoop:
- push si ; Save the starting source index
- push di ; Save the starting dest index
- push ax ; Save the current plane mask
- push bp ; Save the current base pointer
-
- mov ax, ClippedHeight ; Use AL for row counter (0-239)
- mov bx, DecisionY ; Use BX for decision variable
- mov cx, SourceWidth ; Use CX for source width
- mov dx, SourceHeight ; Use DX for source height * 2
- shl dx, 1
- mov bp, DestHeight ; Use BP for dest height * 2
- shl bp, 1
- mov ah, es:[si] ; Get the first source pixel
-
- ALIGN 4 ; Common jump point... align for speed.
- ColumnLoop:
- mov ds:[di], ah ; Draw a pixel
- dec al ; Decrement line counter
- jz DoneWithCol ; See if we're done with this column
- add di, 80 ; Go on to the next screen row
- add bx, dx ; Increment the decision variable
- js ColumnLoop ; Draw this source pixel again
-
- IncSourceRow:
- add si, cx ; Move to the next source pixel
- sub bx, bp ; Decrement the decision variable
- jns IncSourceRow ; See if we need to skip another source pixel
- mov ah, es:[si] ; Get the next source pixel
- jmp ColumnLoop ; Start drawing this pixel
-
- DoneWithCol:
- pop bp ; Restore BP to access variables
- pop ax ; Restore AL = plane mask
- pop di ; Restore DI to top row of screen
- pop si ; Restore SI to top row of source bits
-
- rol al, 1 ; Move to next plane
- adc di, 0 ; Go on to next screen column
- mov dx, SC_INDEX2 ; Tell the VGA what column we're in
- out dx, al ; by updating the map mask register
-
- shl cx, 1 ; CX = SourceWidth * 2
- mov bx, DecisionX ; Use BX for the X decision variable
- add bx, cx ; Increment the X decision variable
- js NextCol ; Jump if we're still in the same source col.
- mov dx, DestWidth ; DX = W * 2
- shl dx, 1
- IncSourceCol:
- inc si ; Move to next source column
- sub bx, dx ; Decrement X decision variable
- jns IncSourceCol ; See if we skip another source column
- NextCol:
- mov DecisionX, bx ; Free up BX for ColLoop
- dec ClippedWidth ; If we're not at last column
- jnz RowLoop ; then do another column
- Done:
- ret ; We're done!
-
- XSCALE1 ENDP
-
- END
-